#!/usr/bin/python
# coding: utf-8
# depends: python-pgsql
#
# qmemorizer is a spaced repetition tool for UNIX console
# Written in Python. Uses PostgreSQL.
#
#     This file is part of qmemorizer.
#     qmemorizer is free software: you can redistribute it and/or modify
#     it under the terms of the GNU General Public License as published by
#     the Free Software Foundation, either version 3 of the License, or
#     (at your option) any later version.

#     qmemorizer is distributed in the hope that it will be useful,
#     but WITHOUT ANY WARRANTY; without even the implied warranty of
#     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#     GNU General Public License for more details.

#     You should have received a copy of the GNU General Public License
#     along with qmemorizer.  If not, see <http://www.gnu.org/licenses/>.


from difflib import Differ
from sys import exit, argv
from os import system, name, environ
from re import sub
from random import randint,choice
from string import split, join
from pyPgSQL import PgSQL
import getopt
from time import time, ctime, localtime, strftime
# command line args
#global databasename
passedRate=4
failedRate=1
forceFailed=False

# table fields; for convenience's sake

nque=0
nans=1
ncat=2
ncre=3
nout=4
nlap=5
ncor=6
nint=7
nlrp=8
nhst=9
nqaf=10
nret=11
nnum=12
nfdr=13
ndsm=14
npri=15
nprr=16
npgr=17
nlgr=17


def connect():
    """Connect to the database"""
    global cnx, cur, databasename
    cnx=PgSQL.connect(database=databasename,user=username,password=password)
    cur=cnx.cursor()
    return cnx

def disconnect():
    """Disconnect from the database"""
    cur.close()
    cnx.commit()

def clear():
    """Clear the console"""
    system("clear")

def createTable(database, name):
    """Create a table in the database to store elements"""
    if len(name)>2:
        print "Creating table '"+name+"'"
        cur.execute("""CREATE TABLE """+name+""" (
            number		serial primary key,
            question	        text,
            answer		text,
            category	        text,
            qafile		text,
            created		integer,
            outstanding	        integer,
            lapses		integer,
            correctAnswers      integer,
            interval	        integer,
            lastRep             integer,
            history		text,
            finalDrill	        boolean,
            retention	        float,
            dismissed           boolean,
            prevInterval        integer,
            prevRealInterval    integer,
            prevGrade           integer,
            lastGrade           integer)""")
        cur.execute("""CREATE TABLE """+tablestats+""" (
            number		serial primary key,
            integerData         integer,
            floatData           float)""")
        cur.execute("""INSERT INTO """+tablestats+""" (integerData) VALUES (0)""")
        cur.execute("""INSERT INTO """+tablestats+""" (integerData) VALUES ("""+str(edate())+""")""")
        try:
            cur.execute("""CREATE INDEX """+name+"""_numberIndex ON """+name+""" (number)""")
            cur.execute("""CREATE INDEX """+name+"""_categoryIndex ON """+name+""" (category)""")
            cur.execute("""CREATE INDEX """+name+"""_qafileIndex ON """+name+""" (qafile)""")
            cur.execute("""CREATE INDEX """+name+"""_outstandingIndex ON """+name+""" (outstanding)""")
            cur.execute("""CREATE INDEX """+name+"""_lapsesIindex ON """+name+""" (lapses)""")
            cur.execute("""CREATE INDEX """+name+"""_correctIndex ON """+name+""" (correctAnswers)""")
            cur.execute("""CREATE INDEX """+name+"""_finalDrillIndex ON """+name+""" (finalDrill)""")
            cur.execute("""CREATE INDEX """+name+"""_dismissedIndex ON """+name+""" (dismissed)""")

        except:
            print "There's something wrong with the table or with indices!"
        cur.close()
        cnx.commit()
    else:
        print "Use a longer table name, please!"

def averageRetention():
    """Count average retention rate"""
    condition=""
    rates=cur.execute("SELECT avg(retention) FROM "+tablename+" "+condition)
    res=cur.fetchall()
    rate=res[0][0]
    try:
        rate=float(rate)
    except:
        return "0.00000"
    return ("%.5f")%rate

def averageLapses():
    """Count average lapses"""
    condition=""
    lapses=cur.execute("SELECT avg(lapses) FROM "+tablename+" "+condition)
    res=cur.fetchall()
    lapses=res[0][0]
    try:
        lapses=("%.5f")%lapses
    except:
        lapses="0.00000"
    return lapses

def averageCorrectAnswers():
    """Count average correct answers"""
    condition=""
    ca=cur.execute("SELECT avg(correctanswers) FROM "+tablename+" "+condition)
    res=cur.fetchall()
    ca=res[0][0]
    try:
        ca=("%.5f")%ca
    except:
        ca="0.00000"
    return ca

def averageInterval():
    """Count average retention rate"""
    condition=""
    rates=cur.execute("SELECT avg(interval) FROM "+tablename+" "+condition)
    res=cur.fetchall()
    interval=res[0][0]
    try:
        interval=("%.5f")%interval
    except:
        interval="0.00000"
    return interval

def creationDate():
    """Get table creation date"""
    condition="WHERE number=2"
    counter=cur.execute("SELECT integerData FROM "+tablestats+" "+condition)
    result=cur.fetchone()[0]
    return result
    

def totalReps():
    """Get total repetition counter"""
    condition="WHERE number=1"
    counter=cur.execute("SELECT integerData FROM "+tablestats+" "+condition)
    result=cur.fetchone()[0]
    return result

def getTotalOutstanding():
    """Get total number of elements outstanding"""
    counter=cur.execute("SELECT count(*) FROM "+tablename+" WHERE outstanding <="+str(edate()))
    result=cur.fetchone()[0]
    return result


def edate():
    return int(time()/86400)

def getSchedule(days):
    """Print schedule for next n days"""
    for a in range(0, days):
        schedule=cur.execute("SELECT number FROM "+tablename+" WHERE outstanding="+str(edate()+a))
        res=len(cur.fetchall())
        print tradDate(edate()+a), res, "elements"

def compareStrings(strg):
    """Lowercase a string."""
    upperlat=u""
    lowerlat=u""
    uppercyr=u""
    lowercyr=u""
    upperlat=unicode("ABCDEFGHIJKLMNOPQRSTUVWXYZßÄÖÜÅÈ", "UTF-8")  # latin alphabet
    lowerlat=unicode("abcdefghijklmnopqrstuvwxyzßäöüåè", "UTF-8")  # 
    uppercyr=unicode("АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ", "UTF-8") # cyrillic alphabet
    lowercyr=unicode("абвгдеёжзийклмнопрстуфхцчшщьыъэюя", "UTF-8") # 
    upperchars=upperlat+uppercyr
    lowerchars=lowerlat+lowercyr
    strg=unicode(strg, "UTF-8")
    strg=sub("[,|.|!|:]", "", strg)
    strg=sub("^ {1,}", "", strg)
    strg=sub(" {1,}$", "", strg)
    strg=sub(" {1,}", " ", strg)
    outstring=u""
    for a in range(0, len(strg)):
        found=False
        for b in range(0, len(upperchars)):
            if strg[a]==upperchars[b]:
                outstring=outstring+lowerchars[b]
                found=True
        if found==False: outstring=outstring+strg[a]
    return outstring

def diffStrings(st1, st2):
    """ This function compares two strings and returns a list. [1] in the list contains marked bytes that aren't present in st1"""
    strg=["", ""]
    st1=unicode(st1, "UTF-8")
    st2=unicode(st2, "UTF-8")
    d = Differ()
    result = list(d.compare(st1, st2))
    for a in range(0, len(result)):
        if result[a][0]==" ":
            strg[0]=strg[0]+result[a][2]
            strg[1]=strg[1]+result[a][2]
        if result[a][0]=="-":
            strg[0]=strg[0]+result[a][2]
        if result[a][0]=="+":
            strg[1]=strg[1]+color_red+result[a][2]+color_off
    return strg


def retentionRate(answers, lapses):
    """Calculate retention rate"""
    if int(answers)>0 and int(answers)>=int(lapses):
        retention=100-(float(int(lapses))/float(int(answers))*100)
    elif int(answers)==0:
        retention=0
    elif int(answers)<int(lapses):
        retention=0
    return ("%.5f")%retention

def getInterval(prevInterval, prevRealInterval, reps, prevGrade,
                   lastInterval, lastRealInterval, lastGrade, created):
    if lastRealInterval>=2 and lastRealInterval<=3:
        newInterval=randint(5,9)
    elif lastRealInterval>3 and lastRealInterval<=5:
        newInterval=randint(8,12)
    elif lastRealInterval>5 and lastRealInterval<=7:
        newInterval=randint(11,15)
    elif lastRealInterval==1:
        newInterval=randint(3,7)
    else:
        newInterval=lastRealInterval+randint(int(lastRealInterval*1.0), int(lastRealInterval*1.4))
    if reps==1:
        newInterval=1
    if lastGrade==1:
        print "FAILED"
        newInterval=randint(1,3)
    if newInterval > 500:
        newInterval=randint(450,600)
    return newInterval


def getIntervalOld(prevInterval, prevRealInterval, reps, prevGrade,
                   lastInterval, lastRealInterval, lastGrade):
    if lastRealInterval>=2 and lastRealInterval<=3:
        newInterval=randint(5,7)
    elif lastRealInterval>3 and lastRealInterval<=5:
        newInterval=randint(6,9)
    elif lastRealInterval>5 and lastRealInterval<=7:
        newInterval=randint(9,12)
    elif lastRealInterval==1:
        newInterval=3
    else:
        newInterval=lastRealInterval+randint(int(lastRealInterval*1.0), int(lastRealInterval*1.4))
    if reps==1:
        newInterval=1
    return newInterval


def getElement(number):
    """Read an element from the database"""
    cur.execute("""SELECT question, answer, category, created, outstanding,
                          lapses, correctAnswers, interval, lastRep,
                          history, qafile, retention, number, finalDrill, dismissed,
                          prevInterval, prevRealInterval, prevGrade, lastGrade
                          FROM """+tablename+""" WHERE number="""+str(number))
    res=cur.fetchone()
    return res

def getOutstandingElements():
    """Determine which elements have to be repeated today"""

    orderby = "ORDER BY number LIMIT 20"

    randomized=False
    cur.execute("""SELECT number FROM """+tablename+"""
                   WHERE outstanding<="""+str(edate())+""" AND dismissed=false """+orderby)
    res=cur.fetchall()
    result=[]
    result2=[]
    for a in range(0, len(res)):
        result.append(res[a][0])
    if randomized==True:
        while len(result)>0:
            a=randint(0,len(result)-1)
            result2.append(result[a])
            del result[a]
        result=result2
    return result

def getFinalDrillElements():
    """Determine which elements are in the final drill queue"""
    cur.execute("""SELECT number FROM """+tablename+""" WHERE finalDrill=true""")
    res=cur.fetchall()
    result=[]
    for a in range(0, len(res)):
        result.append(res[a][0])
    result.sort()
    a=randint(0,1)
    #if a==0:
    #    print "REVERSE final drill"
    #    result.reverse()
    #result.reverse()
  
    return result

def genArray(length, fromValue, toValue):
    n1=fromValue
    n2=toValue
    array=[]
    if n2-n1<length:
        print "ERROR: length value is less than range!\nUse greater length or narrower range!"
        exit(1)
    for a in range(0, length):
        b=randint(n1,n2)
        while array.count(b)<>0:
            b=randint(n1,n2)
        array.append(b)
    return array

def getRandomSet(length):
    """Add a set of random elements to final drill"""
    getNums=elements 
    elnumber=getNums[0]
    maxnumber=getNums[3]
    result=[]
    result=genArray(length, 0, maxnumber)
    return result


def updateElement(element, grade):
    repetitions=int(element[ncor])+int(element[nlap])          # repetitions so far
    realint=str(int(edate())-int(element[nlrp]))  # real interval
    cur.execute("""UPDATE """+tablename+""" SET
                prevGrade="""+str(element[nlgr])+""",
                lastGrade="""+str(grade)+""",
                prevInterval="""+str(element[nint])+""",
                prevRealInterval="""+realint+"""
                WHERE number="""+str(element[nnum]))
    pInt=element[nint]
    pGr=element[nlgr]
    element[nint]=getInterval(element[npri], element[nprr], repetitions, element[npgr], element[nint], int(realint), int(grade), element[ncre])
    modified=False

    corRp=int(element[ncor])
    print "---"
    print element[nhst]
    if corRp>=10 and corRp<20:
        minInt=2
    elif corRp>=20 and corRp<30:
        minInt=3
    elif corRp>=30:
        minInt=4
    else:
        minInt=1
    if grade==failedRate:   # tweak
        element[nint]=randint(1,1)
        print "TWEAKED", element[nint]
        #raw_input()
        
    element[nhst]=element[nhst]+str(edate())+":"+str(grade)+":"+str(element[nint])+hfieldsep #
    element[nout]=int(edate())+int(element[nint])    # WARNING: check!
    print "INT", element[nint], ":", element[nout]
    cur.execute("""UPDATE """+tablename+""" SET
              correctAnswers="""+str(element[ncor])+""",
              lapses="""+str(element[nlap])+""",
              retention="""+retentionRate(element[ncor], element[nlap])+""",
              outstanding="""+str(element[nout])+""",
              interval="""+str(element[nint])+""",
              lastRep="""+str(edate())+""",
              finalDrill="""+str(element[nfdr]).lower()+""",
              history='"""+element[nhst]+"""'
              WHERE number="""+str(element[nnum]))
    cnx.commit()

def holdOn(element):
    """Process post-answering input"""
    print "@@:",  
    hold=raw_input()
    if hold=="":
        return 100
    elif hold=="e":
        editElement(element)
        return 200
    elif hold=="!":
        return 1000
    else:
        return 10

def editElement(elt):
    tempfile=open(filename, "w")
    tempfile.write(elt[nque]+"\n")
    tempfile.write(elt[nans]+"\n")
    tempfile.close()
    system(texteditor+" "+filename)
    tempfile=open(filename, "r")
    newque=tempfile.readline()[:-1]
    newque=sub("'", "\\'", newque)
    newans=tempfile.readline()[:-1]
    newans=sub("'", "\\'", newans)
    tempfile.close()
    cur.execute("""UPDATE """+tablename+""" SET
                      question='"""+newque+"""',
                      answer='"""+newans+"""'
                      WHERE number="""+str(elt[nnum]))
    cnx.commit()

def setFinalDrillFlag(number, value):   # presumably depreciated
    """Set final drill flag to 'on' or 'off'"""
    if value=="on":
        cur.execute("""UPDATE """+tablename+""" SET
                      finalDrill=true
                      WHERE number="""+str(number))
    elif value=="off":
        cur.execute("""UPDATE """+tablename+""" SET
                      finalDrill=false
                      WHERE number="""+str(number))
    cnx.commit()

def incTotalReps():
    """Increment total repetition number by 1"""
    cur.execute("""UPDATE """+tablestats+""" SET integerData=integerData+1 WHERE number=1""")
    cnx.commit()

def getNumberOfElements():
    cur.execute("""SELECT count(number) FROM """+tablename)
    total=cur.fetchall()[0][0]
    cur.execute("""SELECT count(number) FROM """+tablename+""" WHERE dismissed=true""")
    dismissed=cur.fetchall()[0][0]
    cur.execute("""SELECT count(number) FROM """+tablename+""" WHERE dismissed=false""")
    undismissed=cur.fetchall()[0][0]
    cur.execute("""SELECT max(number) FROM """+tablename)
    maximalnumber=cur.fetchall()[0][0]
    return total, undismissed, dismissed, maximalnumber

def forcedExit():
    print "Interrupt by request!"
    exit(0)

def processElement(number, mode):
    """Ask a question with specified repetition (ord/final drill) mode"""
    element=getElement(number)
    nreps=element[ncor]+element[nlap]
    quitAnswering=False
    while quitAnswering==False:
        print "#"+str(element[nnum])+": "+str(tradDate(element[ncre]))+"   ca:"+str(element[ncor])+"+lap:"+str(element[nlap])+"=reps:"+str(nreps)+" left:"+str(len(outstandingSet))+" fdrill:"+str(len(getFinalDrillElements())),
        if randomSetMode==True:
            print " random:"+str(len(randomDrill))
        else:
            print "",
        try:
            print " -- ### -- "+str(edate()-element[nlrp])+" days ago;", str(element[nret])+"% ", element[npri],"->" , element[nint], "-> \n\n"
        except:
            randomDrill.remove(number)
            return 0
        print "-- "+color_blue+" Q: "+color_off+element[nque]
        print "-- "+color_blue+" A:"+color_off,
        grade=0
        ans=raw_input()
        if ans=="!!":
            forcedExit()
        incTotalReps()
        givenAnswer=compareStrings(ans)
        rightAnswer=compareStrings(element[nans])
        if givenAnswer==rightAnswer:  # given answer is right
            if mode=="o":   # mode is ordinary repetition
                grade=passedRate
                print "   "+color_blue+"OK"+color_off+"  "+diffStrings(ans, element[nans])[1]
                holdResult=holdOn(element)
                print holdResult
                if holdResult==1000 or holdResult==100:
                    element[ncor]=int(element[ncor])+1
                    element[nfdr]=False
                    quitAnswering=True
                    updateElement(element, grade)
                    outstandingSet.remove(number)
                elif holdResult==200:
                    element=getElement(number)
                    continue            
                else:
                    continue
            if mode=="f":    # mode is final drill
                print "   "+color_blue+"OK"+color_off+"  "+diffStrings(ans, element[nans])[1]
                grade=passedRate
                holdResult=holdOn(element)
                if holdResult==1000 or holdResult==100:
                    quitAnswering=True
                    if randomSetMode==True:
                        randomDrill.remove(number)
                    else:
                        setFinalDrillFlag(number, "off")
                elif holdResult==200:
                    element=getElement(number)
                    continue            
                else:
                    continue

        elif givenAnswer!=rightAnswer:   # given answer doesn't match
            if mode=="o":                # mode is ordinary repetition
                print " "+color_red+"FAILED"+color_off+"  "+diffStrings(ans, element[nans])[1]
                holdResult=holdOn(element)
                print holdResult
                if holdResult==100:
                    grade=failedRate
                    element[nfdr]=True
                    quitAnswering=True
                    element[nlap]=int(element[nlap])+1
                    updateElement(element, grade)
                    outstandingSet.remove(number)
                elif holdResult==200:
                    element=getElement(number)
                    continue            
                elif holdResult==1000:
                    grade=passedRate
                    element[ncor]=int(element[ncor])+1
                    element[nfdr]=False
                    quitAnswering=True
                    updateElement(element, grade)
                    outstandingSet.remove(number)
                else:
                    continue

            if mode=="f":                # mode is final drill
                grade=failedRate
                print "         "+diffStrings(ans, element[nans])[1]
                holdResult=holdOn(element)
                print holdResult
                if holdResult==1000: 
                    setFinalDrillFlag(number, "off")
                    if randomSetMode==True:
                        if holdResult==1000:
                            randomDrill.remove(number)
                    quitAnswering=True
                elif holdResult==100:
                    quitAnswering=True
                elif holdResult==200:
                    element=getElement(number)
                    continue            
                else:
                    continue

    return grade

def editRecord(number, record, text):
    """Edit records manually. This function isn't essential"""
    cur.execute("""UPDATE """+tablename+""" SET
                      """+record+"""='"""+text+"""'
                      WHERE number="""+str(number))
    cnx.commit()

def removeDuplicateRecords(number):
    """Remove duplicate records in history.  CHECK !!! IT'S BUGGY!"""
    rates=cur.execute("SELECT history FROM "+tablename+" WHERE number="+str(number))
    res=cur.fetchone()
    res[0]=split(res[0], hfieldsep)
    if res[0][-1]=="":
        res[0]=res[0][0:-1]
    length=len(res[0])
    print length
    print res[0]
    if len(res[0])>1:
        for a in range(0, len(res[0])-1):
            print res[0][a][0:5]
            if res[0][a][0:5]==res[0][a+1][0:5]:
                print "duplicate entry removed"
                del res[0][a]
    res[0]=join(res[0], " ")+hfieldsep
    print res[0]
    cur.execute("""UPDATE """+tablename+""" SET
                      history='"""+res[0]+"""'
                      WHERE number="""+str(number))
    cnx.commit()

def getMaxID():
    """Get maximal element ID"""
    if getNumberOfElements()[0]>0:
        rates=cur.execute("SELECT max(number) FROM "+tablename)
        res=cur.fetchall()
        maxid=res[0][0]
    else:
        maxid=0
    return maxid
    
def insertElements(qafile, category):
    created=edate()
    outstanding=created
    lapses=0
    correctAnswers=0
    history=""
    question=["question"]
    answer=["answer"]
    interval=0
    lastRep=created
    retention=0
    finalDrill=False
    dismissed=False
    question=[]
    answer=[]
    prevInterval=0
    prevRealInterval=0
    prevGrade=0
    lastGrade=0
    datafile=open(qafile, "r")
    data=datafile.readlines()
    for a in range(0, len(data)):
        if data[a].endswith("\n"): data[a]=data[a][:-1]
        if data[a].startswith("Q:"):
            data[a]=data[a][3:]
            data[a]=sub("^ ", "", data[a])
            data[a]=sub("'", "\\'", data[a])
            question.append(data[a])
        elif data[a].startswith("A:"):
            data[a]=data[a][3:]
            data[a]=sub("^ ", "", data[a])
            data[a]=sub("'", "\\'", data[a])
            answer.append(data[a])

    if len(question)!=len(answer):
        print "ERROR: inconsistent question-answer data!"
        exit(1)
    else:
        for a in range(0, len(question)):
            number=getMaxID()+1
            cur.execute("""INSERT INTO """+tablename+"""(number, question, answer, category,
                       qafile, created, outstanding, lapses, correctAnswers,
                       interval, lastRep, history, retention, finalDrill,
                       dismissed, prevInterval, prevRealInterval, prevGrade, lastGrade) VALUES ("""+str(number)+""",'"""+question[a]+"""', '"""+answer[a]+"""', '"""+category+"', '"+qafile+"', "+str(created)+", "+str(outstanding)+", "+str(lapses)+", "+str(correctAnswers)+", "+str(interval)+", "+str(lastRep)+", '"+history+"', "+str(retention)+", "+str(finalDrill).lower()+""", """+str(dismissed).lower()+""", """+str(prevInterval)+""", """+str(prevRealInterval)+""", """+str(prevGrade)+""", """+str(lastGrade)+""") """)

        cur.close()
        cnx.commit()

def tradDate(edate):
    return strftime(color_blue+"%b %d, %Y"+color_off, localtime(edate*86400))

def usage():
    print """qmemorizer 0.1 by Andrei Matveyeu,
an universal memory aid and learning tool for UNIX-console written in Python

qmemorizer relies on PostgreSQL database system: http://www.postgresql.org/

This is free software with ABSOLUTELY NO WARRANTY.
Licensed under the terms of the GNU General Public License.

USAGE: qmemorizer -d <database> -t <table> [OPTIONS]

    database        - name of your PostgreSQL database
    table           - table in the database where elements are stored

OPTIONS:
    -c or --create-table - initialize a new table for elements and a data table




examples:
     qmemorizer -d languages -t latin
          mq will connect to the database named 'languages' and use
          the table named 'latin'.
     qmemorizer languages nederlands createtable
          mq will create in the database 'languages' an empty table
          named 'nederlands'
     qm languages english insert ~/irregular-verbs.qa
          mq will add all questions and answers to the table 'english'
                 """

# MAIN :

def main():
    global databasename, tablename, tablestats, cnx, cur, username, password, filename, texteditor
    global psql, fieldsep, hfieldsep, nque, nans, ncat, ncre, nout, nlap, ncor, nint, nlrp, nhst, nqaf, nret, nnum, nfdr, ndsm, npri, nprr, npgr, nlgr
    global passedRate, failedRate, finalDrill, randomDrill, forceFailed, elements, outstandingSet, randomSetMode
    global color_blue, color_red, color_off

# terminal colors:

    color_blue    ="\033[1;34m"
    color_red     ="\033[1;31m"
    color_off     ="\033[0m"

# Other colors you may want to use
# declare them also as global variables:
# global color_grey, color_green, color_yellow, color_magenta, color_cyan, color_darkgrey, color_darkred, color_darkgreen, color_darkyellow, color_darkblue, color_darkmagenta, color_darkcyan
#     color_grey    ="\033[1;30m"
#     color_green   ="\033[1;32m"
#     color_yellow  ="\033[1;33m"
#     color_magenta ="\033[1;35m"
#     color_cyan    ="\033[1;36m"

#     color_darkgrey    ="\033[2;30m"
#     color_darkred     ="\033[2;31m"
#     color_darkgreen   ="\033[2;32m"
#     color_darkyellow  ="\033[2;33m"
#     color_darkblue    ="\033[2;34m"
#     color_darkmagenta ="\033[2;35m"
#     color_darkcyan    ="\033[2;36m"

    clearScreen=True
#    cpsql=' ;" ' # command to close psql call string
    hfieldsep=" "
    texteditor="vim "
    filename=environ['HOME']+"/.qm-insert-file.tmp"


    finalDrill=[]
    outstandingSet=[]
    password=""
    username=environ['USER']
    category=""
    randomSetMode=False
    scheduleMode=False
    finalDrillMode=False
    insertMode=False
    createTableMode=False

    # parsing command line parameters

    try:
        args=argv[1:]
        if len(args)<1:
            usage()
            exit(2)
        opts, args = getopt.getopt(argv[1:], "Cc:d:fhi:p:r:s:t:u:", ["help", "database=", "table=", "user=", "password=", "schedule=", "insert=", "create-table", "category=", "random=","final-drill"])
    except getopt.GetoptError:
        usage()
        print "error!"
        exit(2)
    for o, a in opts:
        if o in ("-r", "--random"):
            randomSetMode=True
            setLength=a
        if o in ("-f", "--final-drill"):
            finalDrillMode=True
        if o in ("-h", "--help"):
            usage()
        if o in ("-d", "--database"):
            databasename = a
        if o in ("-t", "--table"):
            tablename = a
            tablestats="_"+tablename+"_data"
        if o in ("-u", "--user"):
            username = a
        if o in ("-p", "--password"):
            password = a
        if o in ("-s", "--schedule"):
            scheduleMode = True
            scheduleDays = int(a)
        if o in ("-c", "--category"):
            category = a
        if o in ("-i", "--insert"):
            insertMode=True
            qafile = a
        if o in ("-C", "--create-table"):
            createTableMode=True
    connect()
    if insertMode==True:
            insertElements(qafile, category)
            exit(0)
    if createTableMode==True:
            createTable(databasename, tablename)
            exit(0)
    if scheduleMode==True:
        getSchedule(scheduleDays)
        exit(0)


    # print statistics
    elements=getNumberOfElements()
    retValue=averageRetention()
    intValue=averageInterval()
    avgLapses=averageLapses()
    avgCorAns=averageCorrectAnswers()
    totalOutstanding=getTotalOutstanding()
    reps=totalReps()
    collectionDay=edate()-creationDate()+1
    if collectionDay>0:
        elsday=float(elements[0])/float(collectionDay)
    else:
        elsday=elements[0]

    if databasename <> "programming2":
        print elements[0], "elements, ", elements[1], "in learning process, ", elements[2], "dismissed..."
        print "Average retention : ", retValue
        print "Average interval  : ", intValue
        print "Average lapses    : ", avgLapses
        print "Average correct   : ", avgCorAns
        print "Total outstanding : ", totalOutstanding
        print "Total repetitions : ", reps
        print "Learning day      : ", collectionDay
        
        print ("NewElements/day  :  %.5f")%elsday
        repsday=float(reps)/float(collectionDay)
        print ("Repetitions/day  :  %.5f")%repsday

    # random drill mode

    if randomSetMode==True:
        randomDrill=getRandomSet(int(setLength))
        print randomDrill, 
        print "Starting random repetition.", len(randomDrill), "elements queued."
        randomSetMode=True
        while len(randomDrill)>0:
            if len(randomDrill)>=1:
                num=randint(0, len(randomDrill)-1)        # randomize repetitions
                processElement(randomDrill[num], "f")
            else:
                randomDrill=[]
            if clearScreen==True:
                clear()
        exit(2)

    fd=getFinalDrillElements()
    toRepeat=len(fd)
    # ordinary repetition
    if finalDrillMode==False:
        outstandingSet=getOutstandingElements()
        if len(outstandingSet) > 0:
            print len(outstandingSet), "elements queued for ordinary repetition,", len(fd), "for final drill..."
        if len(outstandingSet)>0:
            while len(outstandingSet)>0:  # ordinary repetition loop
                num=-1
                processElement(outstandingSet[num], "o")
                if clearScreen==True:
                    clear()
        finalDrillMode=True

    # final drill

    if finalDrillMode==True:
        finalDrill=getFinalDrillElements()
        if len(finalDrill)>2:
            finalDrill.sort()
        if len(finalDrill)>0:
            print "\nFinal drill..."
        num=len(finalDrill)-1
        while len(finalDrill)>0:  # final drill loop
            print
            processElement(finalDrill[num], "f")
            if clearScreen==True:
                clear()
            finalDrill=getFinalDrillElements()
            num=num-1
            if num<0:
                num=len(finalDrill)-1

    # saving statistics to an external file

    retValue=averageRetention()
    intValue=averageInterval()
    avgLapses=averageLapses()
    avgCorAns=averageCorrectAnswers()

    reps=totalReps()
    collectionDay=edate()-creationDate()+1
    if collectionDay>0:
        elsday=float(elements[0])/float(collectionDay)
    else:
        elsday=elements[0]
    repsday=float(reps)/float(collectionDay)
    repsday=("%.5f")%repsday
    try:
        clr=float(avgCorAns)/float(avgLapses)
    except:
        clr=0.0
    clr=("%.5f")%clr
    disconnect()
    exit(0)


if __name__ == "__main__":
    main()
